﻿// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

namespace Neto;

/// <summary>
///     单线程执行帮助类
/// </summary>
public static class SingleThreadedExecution
{
    // 使用ConcurrentDictionary来跟踪每个键的
    private static readonly ConcurrentDictionary<string, int> ExecutionTracker = new();

    // 公共方法，根据给定的键、操作和最大并发执行次数限制执行
    /// <summary>
    ///     使用指定的并发限制执行操作。
    /// </summary>
    /// <param name="action">要执行的操作委托。</param>
    /// <param name="key">用于区分不同操作的键。</param>
    /// <param name="onError">当操作出现错误时，可选的回调委托来处理异常信息。</param>
    public static void Invoke(Action action, string key, Action<Exception>? onError = null)
    {
        if (string.IsNullOrEmpty(key))
            // 如果key为空或空字符串，不进行并发限制，直接执行操作
            action();
        else
            InvokeInternal(key, () =>
            {
                action();
                return true;
            }, onError);
    }

    // 公共方法，根据给定的键、操作和最大并发执行次数限制执行
    /// <summary>
    ///     使用指定的并发限制异步执行操作。
    /// </summary>
    /// <param name="action">要执行的操作委托。</param>
    /// <param name="key">用于区分不同操作的键。</param>
    /// <param name="onError">当操作出现错误时，可选的回调委托来处理异常信息。</param>
    public static async Task InvokeAsync(Func<Task> action, string key = "", Action<Exception>? onError = null)
    {
        if (string.IsNullOrEmpty(key))
            // 如果key为空或空字符串，不进行并发限制，直接执行操作
            await action();
        else
            await InvokeInternalAsync(key, async () =>
            {
                await action();
                return true;
            }, onError);
    }

    // 公共方法，根据给定的键、操作和最大并发执行次数限制执行，并返回操作的结果
    /// <summary>
    ///     使用指定的并发限制执行操作，并返回操作的结果。
    /// </summary>
    /// <typeparam name="TResult">操作结果的类型。</typeparam>
    /// <param name="function">要执行的操作委托，返回结果。</param>
    /// <param name="key">用于区分不同操作的键。</param>
    /// <param name="onError">当操作出现错误时，可选的回调委托来处理异常信息。</param>
    /// <returns>操作的结果，如果执行失败或达到最大并发数则返回默认值。</returns>
    public static TResult InvokeReturn<TResult>(Func<TResult> function, string key = "",
        Action<Exception>? onError = null)
    {
        if (string.IsNullOrEmpty(key))
            // 如果key为空或空字符串，不进行并发限制，直接执行操作
            return function();

        return InvokeInternal(key, function, onError);
    }

    // 公共方法，根据给定的键、操作和最大并发执行次数限制执行，并返回操作的结果
    /// <summary>
    ///     使用指定的并发限制异步执行操作，并返回操作的结果。
    /// </summary>
    /// <typeparam name="TResult">操作结果的类型。</typeparam>
    /// <param name="function">要执行的操作委托，返回结果。</param>
    /// <param name="key">用于区分不同操作的键。</param>
    /// <param name="onError">当操作出现错误时，可选的回调委托来处理异常信息。</param>
    /// <returns>操作的结果，如果执行失败或达到最大并发数则返回默认值。</returns>
    public static async Task<TResult> InvokeReturnAsync<TResult>(Func<Task<TResult>> function, string key = "",
        Action<Exception>? onError = null)
    {
        if (string.IsNullOrEmpty(key))
            // 如果key为空或空字符串，不进行并发限制，直接执行操作
            return await function();

        return await InvokeInternalAsync(key, async () =>
        {
            var result = await function();
            return result;
        }, onError);
    }


    /// <summary>
    ///     执行指定操作，并提供错误处理和并发控制。
    /// </summary>
    /// <typeparam name="TResult">操作的返回类型。</typeparam>
    /// <param name="key">唯一标识操作的键，用于并发控制。</param>
    /// <param name="operation">要执行的操作。</param>
    /// <param name="onError">当操作发生异常时调用的错误处理行动（可选）。</param>
    /// <returns>操作的返回结果。</returns>
    private static TResult InvokeInternal<TResult>(string key, Func<TResult> operation,
        Action<Exception>? onError = null)
    {
        // 尝试添加键到执行追踪器中，以实现并发控制。
        if (ExecutionTracker.TryAdd(key, 1))
            try
            {
                // 安全执行操作。
                return operation();
            }
            catch (Exception ex)
            {
                // 处理操作中发生的异常，可在此添加自定义的日志或异常处理逻辑。
                // 通过onError委托传递异常信息（如果提供了）。
                onError?.Invoke(ex);
            }
            finally
            {
                // 操作完成后，无论成功还是失败，都移除键以防止并发执行。
                RemoveKeyAfterExecution(key);
            }
        else
            // 如果操作已开始，则返回默认结果。
            return default;

        return default;
    }

    /// <summary>
    ///     异步执行指定操作，并提供错误处理和并发控制。
    /// </summary>
    /// <typeparam name="TResult">操作的返回类型。</typeparam>
    /// <param name="key">唯一标识操作的键，用于并发控制。</param>
    /// <param name="operation">要异步执行的操作。</param>
    /// <param name="onError">当操作发生异常时调用的错误处理行动（可选）。</param>
    /// <returns>操作的异步返回结果。</returns>
    private static async Task<TResult> InvokeInternalAsync<TResult>(string key, Func<Task<TResult>> operation,
        Action<Exception>? onError = null)
    {
        // 尝试添加键到执行追踪器中，以实现并发控制。
        if (ExecutionTracker.TryAdd(key, 1))
            try
            {
                // 安全异步执行操作。
                var result = await operation();

                return result;
            }
            catch (Exception ex)
            {
                // 处理操作中发生的异常，可在此添加自定义的日志或异常处理逻辑。
                // 通过onError委托传递异常信息（如果提供了）。
                onError?.Invoke(ex);
            }
            finally
            {
                // 操作完成后，无论成功还是失败，都移除键以防止并发执行。
                RemoveKeyAfterExecution(key);
            }
        else
            // 如果操作已开始，则返回默认结果。
            return default;

        return default;
    }

    /// <summary>
    ///     在操作完成后，移除执行追踪器中的键。
    /// </summary>
    /// <param name="key">要移除的键。</param>
    private static void RemoveKeyAfterExecution(string key)
    {
        // 尝试移除键，如果移除成功则表示操作已完成。
        if (ExecutionTracker.TryRemove(key, out _))
        {
            // 正常情况，键被成功移除。
        }
        // 异常情况，尝试移除键失败，表示执行追踪器中不存在该键。
        //throw new InvalidOperationException($"Failed to remove key '{key}' after execution.");
    }
}